﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.ServiceModel;
using System.Threading.Tasks;
using System.IO;
using System.Configuration;

namespace DDMountPersistentDrives
{
    class Program
    {
        
        public const string CUSTOM_DATE_FORMAT_SHORT = "dMMMyyyy";
        #region Sync constants
        private const string LOGS_CONTAINER_NAME_CONFIG = "com.dynamicdeploy.ddpersistentdrives.LogsContainer";
        private static string LOGS_CONTAINER_NAME = "ddpersistentdriveslogs";
        #endregion
        #region constants for scheduler
        

        internal const int TIMEINSECSFORINFINITEINITWAIT = 60;
        internal const int BACKUP_INITIAL_WAIT_MILLIS = 300000;
   
        public static string DEPLOYMENT_ID = null;
        public static string ROLE_ID = null;
        public static string INSTANCE_ID = null;

        const string LOG_FILE_NAME = "DDMountPersistentDrives.log";

        public static string AppRoot = "";
        public static string RoleRoot = "";
        public static string SitesRoot = "";
        public static readonly RetryPolicy RETRY_POLICY = RetryPolicies.Retry(3, TimeSpan.Zero);
        #endregion
        static void Main(string[] args)
        {
            DefaultTraceListener drl;
            drl = (DefaultTraceListener)Trace.Listeners["Default"];
            drl.LogFileName = LOG_FILE_NAME;

            drl.TraceOutputOptions = TraceOptions.DateTime;
            Trace.TraceInformation("=====================================================================================================================================");

            Trace.TraceInformation(string.Format("{0}\t{1} in working directory {2}", DateTime.Now.ToString(), "Starting DDPersistentDrives plugin ", Environment.CurrentDirectory));

            try
            {
                //if (RoleEnvironment.IsAvailable)
                //{
                Trace.TraceInformation(string.Format("{0}\t{1}", DateTime.Now.ToString(), "Start SetupConfigurationSettingPublisher()"));
                SetupConfigurationSettingPublisher();
                Trace.TraceInformation(string.Format("{0}\t{1}", DateTime.Now.ToString(), "End SetupConfigurationSettingPublisher()"));
                CloudDriveManagerRole.ENABLE_PERSISTENT_DRIVES = bool.Parse(GetConfigValueAsString("com.dynamicdeploy.ddpersistentdrives.EnablePersistentDrives"));


                if (CloudDriveManagerRole.ENABLE_PERSISTENT_DRIVES)
                {
                    // Register event handler for roleinstance stopping  and changed events
                    if (RoleEnvironment.IsAvailable)
                    {
                        RoleEnvironment.Stopping += new EventHandler<RoleEnvironmentStoppingEventArgs>(RoleEnvironment_Stopping);
                        RoleEnvironment.Changed += new EventHandler<RoleEnvironmentChangedEventArgs>(RoleEnvironment_Changed);

                        DEPLOYMENT_ID = RoleEnvironment.DeploymentId;
                        INSTANCE_ID = RoleEnvironment.CurrentRoleInstance.Id;
                        ROLE_ID = RoleEnvironment.CurrentRoleInstance.Role.Name;
                    }
                    else
                    {
                        DEPLOYMENT_ID = System.Guid.NewGuid().ToString("N");
                        INSTANCE_ID = "1";
                        ROLE_ID = Environment.MachineName;
                    }



                    FillEnvironmentVariables();

                    CloudDriveManagerRole persistentDriveManager = new CloudDriveManagerRole();

                    try
                    {




                        while (!persistentDriveManager.OnStart())
                        {
                            Trace.TraceError("There was an error mounting one of the persistent drives. Will go into infinite loop trying for seconds " + TIMEINSECSFORINFINITEINITWAIT);
                            Thread.Sleep(TimeSpan.FromSeconds(TIMEINSECSFORINFINITEINITWAIT));

                        }



                    }
                    catch (Exception ex)
                    {

                        Trace.TraceError("Error mounting persistent drives. Quitting..." + ex.Message);

                        // Environment.Exit(-1);
                    }

                    //Keep on checking the drives in a loop
                    while (true)
                    {

                        try
                        {
                            var storageAccount = CloudStorageAccount.FromConfigurationSetting(CloudDriveManagerRole.STORAGE_ACCOUNT_SETTING);



                            try
                            {
                                Thread.Sleep(TimeSpan.FromSeconds(Program.TIMEINSECSFORINFINITEINITWAIT * 10));
                                persistentDriveManager.CheckDrives();
                               
                            }
                            catch (Exception ex)
                            {
                                Trace.TraceError("CloudDriveManagerRole fail in Run " + ex.Message);
                            }
                            finally
                            {

                                UploadLogFile();
                            }

                        }
                        catch (Exception ex)
                        {

                            Trace.TraceInformation("Error uploading log file " + ex.Message);
                        }

                                      



                    }

                }
                else
                {

                    Trace.TraceInformation("Persistent drives are not enabled.");

                }


            }
            catch (Exception ex)
            {

                Trace.TraceError("Error in persistent drives " + ex.Message);

            }
        }

        

        #region Role Environment
        private static void SetupConfigurationSettingPublisher()
        {
            // This code sets up a handler to update CloudStorageAccount instances when their corresponding
            // configuration settings change in the service configuration file.
            Trace.TraceInformation("Setting up configuration setting publishing");
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                // Provide the configSetter with the initial value
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));

                RoleEnvironment.Changed += (sender, arg) =>
                {
                    if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
                        .Any((change) => (change.ConfigurationSettingName == configName)))
                    {
                        // The corresponding configuration setting has changed, propagate the value
                        if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
                        {
                            // In this case, the change to the storage account credentials in the
                            // service configuration is significant enough that the role needs to be
                            // recycled in order to use the latest settings. (for example, the 
                            // endpoint has changed)
                            RoleEnvironment.RequestRecycle();
                        }
                    }
                };
            });
        }
        static void RoleEnvironment_Changed(object sender, RoleEnvironmentChangedEventArgs e)
        {
            try
            {
                Trace.TraceError("Roleinstance changed. Will not change synchronization type. Please reboot for changing the synchronization type.");
                if (e.Changes.Any(chg => chg is RoleEnvironmentConfigurationSettingChange))
                {
                    // Perform an action, for example, you can initialize a client, 
                    // or you can recycle the role
                    Trace.TraceError("Any change to configuration will trigger a recycle to maintain consistency");
                    RoleEnvironment.RequestRecycle();
                }

             
            }
            catch (Exception ex)
            {

                Trace.TraceError("Error processing setting change." + ex.Message);

            }

        }

        static void RoleEnvironment_Stopping(object sender, RoleEnvironmentStoppingEventArgs e)
        {
            try
            {
                Trace.TraceWarning("Roleinstance stopping.");


          
            }
            catch (Exception ex)
            {
                Trace.TraceError("Error in RoleEnvironment_Stopping " + ex.Message);
            }
            finally
            {

                UploadLogFile();
            }
            Environment.Exit(-1);
        }
        #endregion

        #region Logs
        static void UploadLogFile()
        {

            try
            {

                string filePath = Path.Combine(Environment.CurrentDirectory, LOG_FILE_NAME);
                if (File.Exists(filePath) && new FileInfo(filePath).Length > 0)
                {
                    string logsContainer = LOGS_CONTAINER_NAME;
                    if (!string.IsNullOrEmpty(GetConfigValueAsString("com.dynamicdeploy.ddpersistentdrives.LogsContainer")))
                    {
                        logsContainer = GetConfigValueAsString("com.dynamicdeploy.ddpersistentdrives.LogsContainer");
                    }
                    var logs = CloudStorageAccount.Parse(GetConfigValueAsString("com.dynamicdeploy.ddpersistentdrives.DrivesStorageAccountConnectionString")).CreateCloudBlobClient().GetContainerReference(logsContainer);
                    logs.CreateIfNotExist();
                    string customDT = DateTime.UtcNow.ToString(CUSTOM_DATE_FORMAT_SHORT).Replace(" ", ""); 
                    var error = logs.GetBlobReference(string.Format("{0}/{1}/{2}/{3}{4}.log", DEPLOYMENT_ID, RoleEnvironment.CurrentRoleInstance.Id, customDT, "DDFileSyncPlugin-", (DateTime.MaxValue - DateTime.UtcNow).Ticks.ToString("d19")));
                    error.Properties.ContentType = "text/plain";


                    Trace.TraceInformation("=====================================================================================================================================");

                    error.UploadFile(filePath, new BlobRequestOptions() { RetryPolicy = RETRY_POLICY, Timeout = TimeSpan.FromSeconds(60) });
                    File.WriteAllText(filePath, string.Empty);
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError("Error uploading log file " + ex.Message);
            }
        }
        #endregion

        

     

        #region Utility Functions
        public static string GetConfigValueAsString(string configName)
        {
            return (RoleEnvironment.IsAvailable)
                                   ? RoleEnvironment.GetConfigurationSettingValue(configName)
                                   : ConfigurationManager.AppSettings[configName];

        }

        public static string ExpandKeywords(string buffer)
        {
            if (RoleEnvironment.IsAvailable)
            {
                buffer = buffer.Replace("$approot$", AppRoot);
                buffer = buffer.Replace("$deploymentid$", RoleEnvironment.DeploymentId);
                buffer = buffer.Replace("$roleinstanceid$", RoleEnvironment.CurrentRoleInstance.Id);
                buffer = buffer.Replace("$computername$", Environment.MachineName);
                buffer = buffer.Replace("$guid$", Guid.NewGuid().ToString());
                buffer = buffer.Replace("$now$", DateTime.Now.ToString());
                buffer = buffer.Replace("$roleroot$", Environment.GetEnvironmentVariable("RoleRoot"));

                if (!string.IsNullOrEmpty(SitesRoot))
                {

                    buffer = buffer.Replace("$sitesroot$", SitesRoot);
                }
            }
            return buffer;
        }
        public static void FillEnvironmentVariables()
        {
            if (RoleEnvironment.IsAvailable)
            {
                try
                {
                    RoleRoot = Environment.GetEnvironmentVariable("RoleRoot");

                    AppRoot = Environment.GetEnvironmentVariable("RoleRoot") + @"\approot";
                    SitesRoot = Environment.GetEnvironmentVariable("RoleRoot") + @"\sitesroot";
                    if (!Directory.Exists(SitesRoot))
                    {
                        SitesRoot = null;
                        // May be WorkerRole
                        //AppRoot = Environment.GetEnvironmentVariable("RoleRoot") + @"\approot";
                        //SitesRoot = AppRoot;
                        if (RoleEnvironment.IsEmulated)
                        {

                            SitesRoot = AppRoot;
                        }
                    }

                    Trace.TraceInformation("Sites Root " + ((!string.IsNullOrEmpty(SitesRoot)) ? SitesRoot : "Seems to be a Worker Role"));
                    Trace.TraceInformation("AppRoot " + AppRoot);


                }
                catch (Exception ex)
                {
                    Trace.TraceError("Error retrieving environment variables and config variables in FillEnvironmentVariables() " + ex.Message);

                }
            }
        }

        #endregion

    }
}
